<?php

namespace App\Console\Commands\Kms\Attendance;

use App\Models\Kms\Attendance\AttendanceDailyGatherModel;
use App\Models\Kms\Attendance\AttendanceDailyPointTraceModel;
use App\Models\Kms\User\KmsUserConfigModel;
use App\Remote\AMap\Track\ServiceRemote;
use App\Remote\AMap\Track\TerminalRemote;
use App\Remote\AMap\Track\TraceRemote;
use App\Service\OpenShop\Feishu\ConfigService;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;

class DailyFieldHandleCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'kms:dailyFieldHandle {date?}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'kms 每日外勤处理';

    public array $fieldConfig = [];

    /**
     * Create a new command instance.
     *
     * @throws ValidationException
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     * @throws ValidationException|GuzzleException
     */
    public function handle(): int
    {
        $this->fieldConfig              = ConfigService::getConfigDetail("kms.user", "attendance")["value"]["field"];
        $date                           = $this->input->getArgument("date") ?? date("Y-m-d", strtotime("-1 days"));
        $this->fieldConfig["startTime"] = strtotime($date . " " . $this->fieldConfig["startTimeFormat"]);
        $this->fieldConfig["endTime"]   = strtotime($date . " " . $this->fieldConfig["endTimeFormat"]);

        $kmsTrackService = $this->getAMapService($this->fieldConfig["aMapServiceName"]);

        KmsUserConfigModel::query()
//                          ->where('userId', 20)
                          ->with([
                              "userRel",
                              "singleFieldDailyGatherRel" => function ($query) use ($date) {
                                  $query->where("date", $date);
                              }
                          ])
                          ->where("fieldSwitch", KmsUserConfigModel::FIELD_SWITCH_ON)
                          ->get()
                          ->each(function (KmsUserConfigModel $kmsUserConfigModel) use ($date, $kmsTrackService) {


                              if (
                                  attributeGet($kmsUserConfigModel, 'singleFieldDailyGatherRel.checkStatus',
                                      null) != AttendanceDailyGatherModel::CHECK_STATUS_FINISH
                              ) {
                                  if (empty($kmsUserConfigModel->singleFieldDailyGatherRel)) {
                                      $kmsUserConfigModel->singleFieldDailyGatherRel = AttendanceDailyGatherModel::query()
                                                                                                                 ->firstOrCreate()
                                                                                                                 ->newModelInstance([
                                                                                                                     "userId"      => $kmsUserConfigModel->userId,
                                                                                                                     "date"        => $date,
                                                                                                                     "checkStatus" => AttendanceDailyGatherModel::CHECK_STATUS_FINISH,
                                                                                                                     "createdAt"   => time()
                                                                                                                 ]);
                                      $kmsUserConfigModel->singleFieldDailyGatherRel->save();
                                      //当天最后打卡时间缺失  状态变更为无考勤
                                      $kmsUserConfigModel->singleFieldDailyGatherRel->status       = AttendanceDailyGatherModel::STATUS_NO_TRACE;
                                      $kmsUserConfigModel->singleFieldDailyGatherRel->missDuration = $this->fieldConfig["endTime"] - $this->fieldConfig["startTime"];
                                  } else {
                                      $maxTotalMissTime     = $this->fieldConfig["uploadConfig"]["totalMissTime"];
                                      $maxReportingInterval = $this->fieldConfig["uploadConfig"]["maxReportingInterval"];

                                      $lastPoint = AttendanceDailyPointTraceModel::query()
                                                                                 ->where("dailyGatherId",
                                                                                     $kmsUserConfigModel->singleFieldDailyGatherRel->id)
                                                                                 ->where("tracePointTime",
                                                                                     ">=",
                                                                                     $this->fieldConfig["startTime"])
                                                                                 ->where("tracePointTime",
                                                                                     "<=",
                                                                                     $this->fieldConfig["endTime"])
                                                                                 ->orderByDesc("id")
                                                                                 ->first();
                                      if (empty($lastPoint)) {
                                          //当天最后打卡时间缺失  状态变更为无考勤
                                          $kmsUserConfigModel->singleFieldDailyGatherRel->status       = AttendanceDailyGatherModel::STATUS_NO_TRACE;
                                          $kmsUserConfigModel->singleFieldDailyGatherRel->missDuration = $this->fieldConfig["endTime"] - $this->fieldConfig["startTime"];
                                      } else {
                                          $firstPoint = AttendanceDailyPointTraceModel::query()
                                                                                      ->where("dailyGatherId",
                                                                                          $kmsUserConfigModel->singleFieldDailyGatherRel->id)
                                                                                      ->where("tracePointTime", ">=",
                                                                                          $this->fieldConfig["startTime"])
                                                                                      ->where("tracePointTime", "<=",
                                                                                          $this->fieldConfig["endTime"])
                                                                                      ->orderBy("id")->first();

                                          //每日上班时间和第一次打卡时间间隔
                                          $startBetweenTime = $firstPoint->tracePointTime - $this->fieldConfig["startTime"];
                                          if ($startBetweenTime > $maxReportingInterval) {
                                              //异常时间增加
                                              $kmsUserConfigModel->singleFieldDailyGatherRel->missDuration = $kmsUserConfigModel->singleFieldDailyGatherRel->missDuration + $startBetweenTime;
                                          }
                                          //停止轨迹上报时间和最后打卡时间间隔
                                          $stopBetweenTime = $this->fieldConfig["endTime"] - $lastPoint->tracePointTime;
                                          if ($stopBetweenTime > $maxReportingInterval) {//间隔大于最大上报时间 算作异常时间
                                              //异常时间增加
                                              $kmsUserConfigModel->singleFieldDailyGatherRel->missDuration = $kmsUserConfigModel->singleFieldDailyGatherRel->missDuration + $stopBetweenTime;
                                          }
                                          if ($kmsUserConfigModel->singleFieldDailyGatherRel->missDuration >= $maxTotalMissTime) {
                                              //异常时间大于最大累计异常时间
                                              $kmsUserConfigModel->singleFieldDailyGatherRel->status = AttendanceDailyGatherModel::STATUS_ERROR;//状态变更为异常
                                          }
                                      }

                                      $trackTerminal = $this->getAMapTerminal(
                                          $kmsTrackService["sid"],
                                          "kms_attendance_uid_{$kmsUserConfigModel->userId}",
                                          "{$kmsUserConfigModel->userRel->name}-{$kmsUserConfigModel->userRel->nickname}"
                                      );

                                      if ($kmsUserConfigModel->singleFieldDailyGatherRel->aMapTRid > 0) {
                                          TraceRemote::delete(
                                              $kmsTrackService["sid"],
                                              $trackTerminal["tid"],
                                              $kmsUserConfigModel->singleFieldDailyGatherRel->aMapTRid
                                          );
                                      }

                                      $aMapTrace = $this->getAMapTrace(
                                          $kmsTrackService["sid"],
                                          $trackTerminal["tid"],
                                          "trace_uid_{$kmsUserConfigModel->userId}_date_{$date}"
                                      );

                                      AttendanceDailyPointTraceModel::query()
                                                                    ->where("dailyGatherId",
                                                                        $kmsUserConfigModel->singleFieldDailyGatherRel->id)
                                                                    ->where("tracePointTime", ">=",
                                                                        $this->fieldConfig["startTime"])
                                                                    ->where("tracePointTime", "<=",
                                                                        $this->fieldConfig["endTime"])
                                                                    ->orderBy("id")
                                                                    ->chunkById(100,
                                                                        function (Collection $collection) use (
                                                                            $kmsTrackService,
                                                                            $trackTerminal,
                                                                            $aMapTrace
                                                                        ) {
                                                                            $insert = $collection->map(function (
                                                                                AttendanceDailyPointTraceModel $pointTraceModel
                                                                            ) {
                                                                                $isExcursionPoint = false;
                                                                                $accuracy         = 0;
                                                                                if (strlen($pointTraceModel->extra) > 0) {
                                                                                    $extra    = json_decode($pointTraceModel->extra,
                                                                                        true);
                                                                                    $accuracy = (int)($extra["accuracy"] ?? 0);
                                                                                    if (in_array($extra["locationType"] ?? 0,
                                                                                            [
                                                                                                AttendanceDailyPointTraceModel::LOCATION_TYPE_2,
                                                                                                AttendanceDailyPointTraceModel::LOCATION_TYPE_4,
                                                                                            ])) {
                                                                                        $isExcursionPoint = true;
                                                                                    }
                                                                                }
                                                                                if ($isExcursionPoint) {
                                                                                    return [];
                                                                                }
                                                                                return array_merge([
                                                                                    "location"   => "{$pointTraceModel->longitude},{$pointTraceModel->latitude}",
                                                                                    "locatetime" => $pointTraceModel->tracePointTime * 1000,
                                                                                ],
                                                                                    $accuracy > 0 ? ["accuracy" => $accuracy] : []);
                                                                            })->filter()->values()->toArray();

                                                                            TraceRemote::pointUpload(
                                                                                $kmsTrackService["sid"],
                                                                                $trackTerminal["tid"],
                                                                                $aMapTrace["trid"],
                                                                                $insert
                                                                            );
                                                                        });

                                      $kmsUserConfigModel->singleFieldDailyGatherRel->aMapTRid = $aMapTrace["trid"];
                                  }

                                  $kmsUserConfigModel->singleFieldDailyGatherRel->checkStatus = AttendanceDailyGatherModel::CHECK_STATUS_FINISH;
                                  $kmsUserConfigModel->singleFieldDailyGatherRel->updatedAt   = time();
                                  $kmsUserConfigModel->singleFieldDailyGatherRel->save();
                              }
                          });

        logInfo("【 {$date} 】考勤每日核验脚本执行完毕", collect(["date" => $date]), true, false);

        return 0;
    }

    /**
     * @return mixed
     * @throws GuzzleException
     * @throws ValidationException
     */
    public static function getAMapService(string $aMapServiceName)
    {
        $trackService    = ServiceRemote::list();
        $kmsTrackService = collect($trackService["results"])->where("name", $aMapServiceName)
                                                            ->first();
        if (empty($kmsTrackService)) {
            $kmsTrackService = ServiceRemote::add($aMapServiceName, "kms外勤轨迹服务");
        }
        return $kmsTrackService;
    }

    /**
     * @param int $sid
     * @param string $name
     * @param string $desc
     * @return mixed
     * @throws GuzzleException
     * @throws ValidationException
     */
    public static function getAMapTerminal(int $sid, string $name, string $desc)
    {
        $trackTerminal = TerminalRemote::list($sid, $name);
        if (empty($trackTerminal)) {
            return TerminalRemote::add($sid, $name, $desc);
        } else {
            return current($trackTerminal["results"]);
        }
    }

    /**
     * @param int $sid
     * @param int $tid
     * @param string $tRName
     * @return mixed
     * @throws GuzzleException
     * @throws ValidationException
     */
    public static function getAMapTrace(int $sid, int $tid, string $tRName)
    {
        return TraceRemote::add($sid, $tid, $tRName);
    }
}
